import { defineComponent, watch } from "vue";
import { cloneDeep } from 'lodash'
import { PropType, ref } from "vue";
import { nodeKey, renderFunc, RequiredTreeNodeOptions, TreeNodeInstance, TreeNodeOptions } from "./types";
import './index.scss'
import { updateUpWards, updateDownWards } from "./utils";

export default defineComponent({
  name: 'Tree',
  props: {
    source: { type: Array as PropType<TreeNodeOptions[]>, default: () => []},
    showCheckBox: { type: Boolean, default: false },
    checkStrictly: { type: Boolean, default: false },
    render: Function as PropType<renderFunc>,
    lozyLoad: Function as PropType<(node: RequiredTreeNodeOptions, callback: (children: TreeNodeOptions[]) => void) => void>
  },
  setup(props, { emit, slots, expose }) {
    const loading = ref(false);
    const selectKey = ref<nodeKey>('')
    const flatList = ref<RequiredTreeNodeOptions[]>([]);
    function flattenTree (source: TreeNodeOptions[]): RequiredTreeNodeOptions[] {
      const result: RequiredTreeNodeOptions[] = [];
      const recursion = (list: TreeNodeOptions[], level = 0, parent: RequiredTreeNodeOptions | null = null): RequiredTreeNodeOptions[] => {
        return list.map(item => {
          const node : RequiredTreeNodeOptions = {
            ...item,
            level,
            loading: false,
            disabled: item.disabled || false,
            expanded: item.expanded || false,
            selected: item.selected || false,
            checked: item.checked || parent?.checked || false,
            hasChildren: item.hasChildren || false,
            parentKey: parent?.nodeKey || null,
            children: item.children || []
          };
          if (node.selected) {
            selectKey.value = node.nodeKey;
          }
          result.push(node);
    
          if (item.expanded && node.children) {
            node.children = recursion(node.children, level + 1, node)
          }
          return node;
        });
      };
      if (source.length) {
        recursion(source)
      }
      return result;
    }
    watch(() => props.source, newVal => {
      flatList.value = flattenTree(newVal);
      // console.log('watch', selectKey.value)
    }, {immediate: true })


    const expandNode = (node: RequiredTreeNodeOptions, children: TreeNodeOptions[] = []) => {
      const trueChildren = children.length ? children : cloneDeep(node.children);
       node.children = trueChildren.map(item => {
         return {
          ...item,
          level: item.level || node.level + 1,
          loading: false,
          disabled: item.disabled || false,
          expanded: item.expanded || false,
          selected: item.selected || false,
          hasChildren: item.hasChildren || false,
          checked: item.checked ?? node?.checked,
          parentKey: node?.nodeKey || null,
          children: item.children || []
         }
       });
       const targetIndex = flatList.value.findIndex(item => item.nodeKey === node.nodeKey);
       if (targetIndex > -1) {
         flatList.value.splice(targetIndex + 1, 0, ...(node.children as RequiredTreeNodeOptions[]))
       }
    }

    const collapseNode = (node: RequiredTreeNodeOptions) => {
      const delKeys: nodeKey[] = [];
      const recursion = (currentNode: RequiredTreeNodeOptions) => {
        if (currentNode.children.length) {
          currentNode.children.forEach(item => {
            delKeys.push(item.nodeKey);
            if (item.expanded) {
              item.expanded = false;
              recursion(item as RequiredTreeNodeOptions);
            }
          })
        }
      };
      recursion(node)
      if (delKeys.length) {
        flatList.value = flatList.value.filter(item => !delKeys.includes(item.nodeKey))
      }
    }

    const handleToggleExpand = (node: RequiredTreeNodeOptions) => {
      if (loading.value) return;
      node.expanded = !node.expanded;
      if (node.expanded) {  
        if (node.children.length) { // 首次展开 children 可能是用户自带的
          expandNode(node)
        } else {
          if (props.lozyLoad && node.hasChildren) { // 懒加载
            node.loading = true;  // 控制图标
            loading.value = true; // 防止重复点击
            props.lozyLoad(node, children => {
              if (children.length) {
                expandNode(node, children)
              }
              node.loading = false;  // 控制图标
              loading.value = false; // 防止重复点击
            });
          }
          else {
            node.expanded = !node.expanded;
          }
        // 懒加载
        }
      } else {  // 收起
        collapseNode(node)
      }
    };

    const handleSelectChange = (node:RequiredTreeNodeOptions) => {
      node.selected = !node.selected;
      let newSelectKey: nodeKey = "";
      if (selectKey.value !== node.nodeKey)  {
        const preSelectIndex = flatList.value.findIndex(n => n.nodeKey === selectKey.value);
        if (preSelectIndex > -1) flatList.value[preSelectIndex].selected = false;
        newSelectKey = node.nodeKey;
      }
      selectKey.value = newSelectKey;
      emit('select-change', node)
    }

    const handleCheckChange = ([checked, node]: [boolean, RequiredTreeNodeOptions]) => {
      node.checked = checked;
      if (!props.checkStrictly) {
        updateDownWards(checked, node);
        updateUpWards(node, flatList.value);
      }
      emit('check-change', node);
    };

    const nodeRefs= ref<TreeNodeInstance[]>([]);
    const setNodesRefs = (index: number, node: TreeNodeInstance) => {
      if (node) {
        nodeRefs.value[index] = node;
      }
    }

    expose({
      getSelectedNode: (): RequiredTreeNodeOptions| undefined => {
      return flatList.value.find(item => item.selected);
      },
      getCheckedNodes: (): RequiredTreeNodeOptions[] => {
        return flatList.value.filter(item => item.checked);
      },
      getHalfCheckedNodes: (): RequiredTreeNodeOptions[] => {
        return nodeRefs.value.filter(item => item.halfChecked()).map(item => item.node)
      }
    })

    return () => {
      return (
        <div class="ant-tree-wrap">

          <div class="ant-tree">
            {
              flatList.value.map((node, index) => {
                return <tree-node 
                  key={ node.nodeKey }
                  //@ts-ignore
                  ref={ setNodesRefs.bind(null, index) }
                  node={ node }
                  render={ props.render }
                  iconSlot={ slots.icon }
                  showCheckBox={ props.showCheckBox }
                  onToggleExpand={ handleToggleExpand }
                  onSelectChange={ handleSelectChange }
                  onCheckChange={ handleCheckChange }
                  />
                })
              }

          </div>

        </div>
      )
    }
  }
})
